﻿package src {
	
	import flash.display.Bitmap;
	import flash.display.CapsStyle;
	import flash.display.Sprite;
	import flash.display.MovieClip;
	import flash.display.Graphics;
	import flash.display.BitmapData;
	import flash.events.MouseEvent;
	import flash.events.KeyboardEvent;
	import flash.geom.Point;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.text.TextFormat;
	import flash.utils.ByteArray;
	import flash.net.URLRequest;
	import flash.net.URLVariables;
	import flash.net.URLRequestMethod;
	import flash.net.navigateToURL;
	import flash.system.System;
	
	import fl.transitions.Tween;
	import fl.transitions.TweenEvent;
	import fl.transitions.easing.*;
	import fl.controls.UIScrollBar;
	import fl.events.ScrollEvent;
	
	import src.uipackage.ui.*;
	import src.uipackage.ui.events.*;
	import src.utils.*;
	
	/**
	 * The document class for the Flash file.
	 * Responsible for most of the applications work
	 */
	public class Viewer extends ViewerEngine {
		
		// Main GUI elements and variables:
		
		/**
		 * The Sprite where the image on the left is drawn on
		 */
		private var view:Sprite = new Sprite();
		
		/**
		 * The Sprite for sequence row names
		 */
		private var seqRowNames:Sprite = new Sprite();
		
		/**
		 * The Sprite for horizontal row indexes
		 */
		private var seqRowHorizIndexes:Sprite = new Sprite();
		
		/**
		 * True if view (the image on the left) is being dragged
		 */
		private var viewDragged:Boolean = false;
		
		/**
		 * The container Sprite for UI
		 */
		private var ui:Sprite = new Sprite();
		
		/**
		 * Holds the MainFrame() that is part of the visualizer.fla's library
		 */
		private var mainFrame:MovieClip = new MainFrame();
		
		/**
		 * Holds the fla's library MouseOverWindow()
		 */
		private var mouseOverWindow:MovieClip = new MouseOverWindow();
		
		/**
		 * Scrollbar for the legend tab
		 */
		private var tabsScrollbar:UIScrollBar = new UIScrollBar();
		
		/**
		 * A mask for the scrollbar
		 */
		private var tabContentMask:Sprite;
		
		/**
		 * All possible tabs to be selected
		 */
		private var tabsNames:Array = ["legendTab", "settingsTab", "viewTab", "infoTab", "conservTab"];
		
		/**
		 * The name of the tab currently selected
		 */
		private var tabSelected:String;
		
		/**
		 * LegendTab object
		 */
		public var legend:LegendTab;
		
		/**
		 * SettingsTab object
		 */
		public var settings:SettingsTab;
		
		/**
		 * ViewTab object
		 */
		public var viewTab:ViewTab;
		
		/**
		 * InfoTab object
		 */
		public var info:InfoTab;
		
		/**
		 * ConservvationTab object
		 */
		public var conservTab:ConservationTab;
		
		// Redrawing:
		
		/**
		 * True if view should be redrawn
		 */
		public var viewIsDirty:Boolean = false;
		
		/**
		 * True if UI should be redrawn
		 */
		public var uiIsDirty:Boolean = false;
		
		/**
		 * True if sequence row names are not hidden
		 */
		public var seqRowNamesVisible:Boolean = true;
		
		// Zoom:
		
		/**
		 * The current zoom level
		 */
		public var zoomLevel:int = 1;
		
		/**
		 * Previous zoom level, needed for zooming in-out
		 */
		public var oldZoomLevel:int;
		
		/**
		 * All possible zoom levels
		 */
		private var zoomLevels:Array = [1, 2, 3, 4];
		
		// Left symbol and upper row:
		
		/**
		 * The leftmost nucleotide index
		 */
		public var globalLeftSymbolIndex:int = 0;
		
		/**
		 * The index of the top sequence row
		 */
		public var globalUpperRowIndex:int = 0;
		
		// To help dragging:
		
		/**
		 * Mouse coordinates before dragging starts
		 */
		public var oldMouseCoordinates:Point = new Point();
		
		// The miniview:
		
		/**
		 * The MovieClip object that represents miniview
		 */
		public var miniViewRect:MovieClip = new MovieClip();
		
		/**
		 * True if miniview is being used
		 */
		public var miniViewDragging:Boolean = false;
		
		// The zoomIn rectangle:
		
		/**
		 * The red rectangle that is animated when zooming in
		 */
		private var zoomInRect:Sprite;
		
		// Matches drawing properties:
		
		/**
		 * Matches inner alpha value
		 */
		public var innerAlpha:Number = 0.33;
		
		/**
		 * Called by engine when loading is finished. Initializes
		 * the viewer, adds display objects, calls drawing functions
		 * and adds event listeners
		 */
		public override function initViewer():void {
			trace("init!");
			while (numChildren > 0) removeChildAt(0);
			addChild(view);
			addChild(ui);
			
			drawView();
			view.addEventListener(MouseEvent.MOUSE_DOWN, dragSeqView);
			addEventListener(MouseEvent.MOUSE_UP, stopDragging);
			stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDown);
			//stage.addEventListener(MouseEvent.MOUSE_WHEEL, mouseWheel);
			initUI();
			drawUI();
			mouseOverWindow.visible = false;
			addChild(mouseOverWindow);
		}
		
		/**
		 * Event handler for mouse wheel event. Not used and the event is not
		 * listened.
		 * @param	e
		 */
		private function mouseWheel(e:MouseEvent):void {
			//
		}
		
		/**
		 * Event handler for key down event. Listens for E to export view
		 * @param	e
		 */
		private function keyDown(e:KeyboardEvent):void {
			trace("Key pressed. code=" + e.keyCode + " and ctrl=" + e.ctrlKey);
			
			switch(e.keyCode) {
				case 69:
					//if (e.ctrlKey) exportView();
					exportView(2);
					break;
				default:
					break;
			}
		}
		
		/**
		 * Calls all tabs .draw() function
		 */
		private function drawUI():void {
			legend.draw();
			settings.draw();
			viewTab.draw();
			info.draw();
			conservTab.draw();
		}
		
		/**
		 * Initializes UI elements
		 */
		private function initUI():void {
			mainFrame.x = mainFrame.y = 1;
			ui.addChild(mainFrame);
			
			// Scrollbar
			tabContentMask = new Sprite();
			var tcmg:Graphics = tabContentMask.graphics;
			tcmg.beginFill(0xFF0000);
			tcmg.drawRect(0, 0, 130, 555);
			tcmg.endFill();
			tabContentMask.x = 753;
			tabContentMask.y = 40;
			tabsScrollbar.y = 33;
			tabsScrollbar.x = 885;
			tabsScrollbar.height = 568;
			tabsScrollbar.addEventListener(ScrollEvent.SCROLL, tabsScrolled);
			ui.addChild(tabContentMask);
			ui.addChild(tabsScrollbar);
			
			// Tabs
			legend = new LegendTab(this, motifMatchesArray);
			settings = new SettingsTab(this);
			viewTab = new ViewTab(this);
			info = new InfoTab(this);
			conservTab = new ConservationTab(this);
			legend.draw();
			settings.draw();
			viewTab.draw();
			info.draw();
			conservTab.draw();
			ui.addChild(legend);
			ui.addChild(settings);
			ui.addChild(viewTab);
			ui.addChild(info);
			ui.addChild(conservTab);
			initTabButtons(); 
			legend.x = settings.x = viewTab.x = info.x = conservTab.x = 766;
			legend.y = settings.y = viewTab.y = info.y = conservTab.y = 40;
			selectTab(null, "legendTab");
		}
		
		/**
		 * Checks if the views indexes are too big or small. Called when
		 * zooming in-out or navigating
		 */
		private function checkIndexes():void {
			if(seqData.getSeqRowLength() > getNumberOfLettersInRow(zoomLevel)) {
				globalLeftSymbolIndex -= (getNumberOfLettersInRow(zoomLevel) - getNumberOfLettersInRow(oldZoomLevel)) * 0.5;
				if (globalLeftSymbolIndex < 0) 
					globalLeftSymbolIndex = 0;
				if (globalLeftSymbolIndex > seqData.getSeqRowLength() - getNumberOfLettersInRow(zoomLevel))
					globalLeftSymbolIndex = seqData.getSeqRowLength() - getNumberOfLettersInRow(zoomLevel);
			}
			else globalLeftSymbolIndex = 0;
			if (seqData.getNumberOfSeqRows() > getNumberOfRowsOnView(zoomLevel)) {
				globalUpperRowIndex -= (getNumberOfRowsOnView(zoomLevel) - getNumberOfRowsOnView(zoomLevel - 1)) * 0.5;
				if (globalUpperRowIndex < 0) 
					globalUpperRowIndex = 0;
				if (globalUpperRowIndex > seqData.getNumberOfSeqRows() - getNumberOfRowsOnView(zoomLevel))
					globalUpperRowIndex = seqData.getNumberOfSeqRows() - getNumberOfRowsOnView(zoomLevel);
			}
			else globalUpperRowIndex = 0;
		}
		
		/**
		 * Zoom out
		 * @param	e
		 */
		public function zoomOut(e:MouseEvent = null):void {
			checkIndexes();
			viewIsDirty = true;
			uiIsDirty = true;
		}
		
		/**
		 * Creates the red rectangle to animate zooming in
		 */
		public function initZoomInRect():void {
			var oldLeftSymbolIndex:int = globalLeftSymbolIndex;
			var oldUpperRowIndex:int = globalUpperRowIndex;
			zoomInRect = new Sprite();
			var g:Graphics = zoomInRect.graphics;
			g.lineStyle(2, 0xFF0000, 0.75);
			var _width:Number = getLetterSize(oldZoomLevel) * getNumberOfLettersInRow(zoomLevel);
			var _height:Number = getSpaceBetweenRows(oldZoomLevel) * getNumberOfRowsOnView(zoomLevel);
			var _x:Number = getLetterSize(oldZoomLevel) * (globalLeftSymbolIndex - oldLeftSymbolIndex);
			var _y:Number = getSpaceBetweenRows(oldZoomLevel) * (globalUpperRowIndex - oldUpperRowIndex);
			g.moveTo(_x, _y);
			_x += _width;
			g.lineTo(_x, _y);
			_y += _height;
			g.lineTo(_x, _y);
			_x -= _width;
			g.lineTo(_x, _y);
			_y -= _height;
			g.lineTo(_x, _y);
			view.addChild(zoomInRect);
			zoomInRect.startDrag(true);
			
			view.addEventListener(MouseEvent.CLICK, zoomIn);
		}
		
		/**
		 * Start zooming in
		 * @param	e
		 */
		private function zoomIn(e:MouseEvent):void {
			view.removeEventListener(MouseEvent.CLICK, zoomIn);
			globalLeftSymbolIndex += Math.round(zoomInRect.x / getLetterSize(oldZoomLevel));
			globalUpperRowIndex += Math.round(zoomInRect.y / (getLetterSize(oldZoomLevel) + getSpaceBetweenRows(oldZoomLevel)));
			//checkIndexes();
			zoomInRect.stopDrag();
			var fader:Tween = new Tween(zoomInRect, "alpha", Regular.easeOut, 1, 0, 15);
			fader.addEventListener(TweenEvent.MOTION_FINISH, finishZoomIn);
		}
		
		/**
		 * Finish zooming in
		 * @param	e	-	TweenEvent
		 */
		private function finishZoomIn(e:TweenEvent):void {
			uiIsDirty = true;
			viewIsDirty = true;
		}
		
		/**
		 * A function to process ScrollEvents thrown by the tabs scrollbar
		 * @param	e
		 */
		private function tabsScrolled(e:ScrollEvent):void {
			legend.y = 40 - e.position;
		}
		
		/**
		 * Initialize tab buttons
		 */
		private function initTabButtons():void {
			for (var i:int = 0; i < tabsNames.length; i++) {
				var tabBtn:MovieClip = mainFrame[tabsNames[i]];
				tabBtn.tabName = tabsNames[i];
				tabBtn.buttonMode = true;
				tabBtn.addEventListener(MouseEvent.ROLL_OVER, mouseOverTabBtn);
				tabBtn.addEventListener(MouseEvent.ROLL_OUT, mouseOutTabBtn);
				tabBtn.addEventListener(MouseEvent.CLICK, selectTab);
			}
		}
		
		/**
		 * Mouse out tab button handler
		 * @param	e
		 */
		private function mouseOutTabBtn(e:MouseEvent):void {
			if (e.currentTarget.tabName == tabSelected) return;
			e.currentTarget.alpha = 0.5;
		}
		
		/**
		 * Mouse over tab button handler
		 * @param	e
		 */
		private function mouseOverTabBtn(e:MouseEvent):void {
			e.currentTarget.alpha = 1;
		}
		
		/**
		 * Select another tab
		 * @param	e			-	the mouse event
		 * @param	tabName		-	name of the tab to check
		 */
		public function selectTab(e:MouseEvent = null, tabName:String = ""):void {
			if (e != null) tabName = e.currentTarget.tabName;
			tabSelected = tabName;
			for (var i:int = 0; i < tabsNames.length; i++) mainFrame[tabsNames[i]].alpha = 0.5;
			mainFrame[tabName].alpha = 1;
			
			legend.visible = false;
			legend.mask = null;
			settings.visible = false;
			viewTab.visible = false;
			info.visible = false;
			conservTab.visible = false;
			tabsScrollbar.visible = false;
			tabsScrollbar.enabled = false;
			switch(tabName) {
				case "legendTab":
					if (legend.height > tabContentMask.height - 25) {
						tabsScrollbar.visible = true;
						tabsScrollbar.enabled = true;
						tabsScrollbar.setScrollProperties(legend.height-tabContentMask.height, 0, legend.height-tabContentMask.height + 25, 25);
					}
					legend.visible = true;
					tabContentMask.visible = true;
					legend.mask = tabContentMask;
					break;
				case "settingsTab":
					tabContentMask.visible = false;
					settings.visible = true;
					break;
				case "viewTab":                           
					tabContentMask.visible = false;
					viewTab.visible = true;
					break;
				case "infoTab":
					tabContentMask.visible = false;
					info.visible = true;
					break;
				case "conservTab":
					tabContentMask.visible = false;
					conservTab.visible = true;
				default: break;
			}
		}
		
		/**
		 * Draws the view object. Adds conservation, sequence
		 * rows, motifs and indexes.
		 */
		public function drawView():void {
			//trace("drawView leftIndex=" + globalLeftSymbolIndex + " and globalUpperRowIndex=" + globalUpperRowIndex);
			while (view.numChildren > 0) view.removeChildAt(0);
			
			// Draw the sequence:
			seqData.draw(globalLeftSymbolIndex, globalUpperRowIndex, zoomLevel);
			view.addChild(seqData);
			
			// To optimize and check for loading errors:
			if (motifMatchesArray == null || motifMatchesArray.length == 0) return;
			
			// The matches:
			var matchesView:Sprite = new Sprite();
			view.addChild(matchesView);
			for each(var m:MotifMatches in motifMatchesArray) {
				var matches:Array = m.getAllMatches();
				if (matches == null) continue;
				for each(var match:MotifMatch in matches) {
					if (match == null) continue;
					if (match.getScore() < m.getFilter()) continue;
					if (match.getBeginIndex() < globalLeftSymbolIndex || 
						match.getEndIndex() >= globalLeftSymbolIndex + getNumberOfLettersInRow(zoomLevel) ||
						match.getSequenceNumber() < globalUpperRowIndex ||
						match.getSequenceNumber() > globalUpperRowIndex + getNumberOfRowsOnView(zoomLevel) - 1) 
						continue;
					match.draw(zoomLevel, m, getLetterSize(zoomLevel)<=1?1:innerAlpha, getLetterSize(zoomLevel));
					match.x = (match.getBeginIndex() - globalLeftSymbolIndex) * getLetterSize(zoomLevel);
					match.y = getSpaceBetweenRows(zoomLevel) + ((match.getSequenceNumber() - globalUpperRowIndex) * getSpaceBetweenRows(zoomLevel));
					
					if (!match.hasEventListener(MouseEvent.MOUSE_OVER)) 
						match.addEventListener(MouseEvent.MOUSE_OVER, mouseOverMotifMatch, false, 0, true);
						//match.addEventListener(MouseEvent.MOUSE_OVER, mouseOverMotifMatch);
						// TODO - not really sure about the false,0,true thing
					if (!match.hasEventListener(MouseEvent.MOUSE_OUT))
						match.addEventListener(MouseEvent.MOUSE_OUT, mouseOutMotifMatch,false,0,true);
						//match.addEventListener(MouseEvent.MOUSE_OUT, mouseOutMotifMatch);
					
					matchesView.addChild(match);
				}
			}
			
			// Position:
			view.x = view.y = 3;
			
			// Update the seq row names:
			seqRowNames = seqData.drawNames(globalUpperRowIndex, zoomLevel);
			seqRowNames.x = 3;
			if (zoomLevel <= 2) seqRowNames.y = 5;
			else if (zoomLevel == 3) seqRowNames.y = 1;
			else seqRowNames.y = 3;
			if (!seqRowNamesVisible) seqRowNames.visible = false;
			view.addChild(seqRowNames);
			
			seqRowHorizIndexes = drawHorizIndexes();
			view.addChild(seqRowHorizIndexes);
		}
		
		/**
		 * Draw horizontal indexes according to globalLeftSymbolIndex
		 * @return
		 */
		private function drawHorizIndexes():Sprite {
			var sprite:Sprite = new Sprite();
			var _x:Number = 0;
			var lettersInRow:int = getNumberOfLettersInRow(zoomLevel);
			for (var i:int = 0; i < lettersInRow; i += lettersInRow * 0.1) {
				_x = i * getLetterSize(zoomLevel);
				var tf:TextField = new TextField();
				tf.x = _x;
				tf.selectable = false;
				tf.defaultTextFormat = new TextFormat("Arial",9);
				tf.text = String(i + globalLeftSymbolIndex);
				sprite.addChild(tf);
			}
			return sprite;
		}
		
		/**
		 * When match gets MOUSE_OVER event
		 * @param	e
		 */
		private function mouseOverMotifMatch(e:MouseEvent):void {
			var match:MotifMatch = e.target as MotifMatch;
			mouseOverWindow.visible = true;
			var fieldFormat:TextFormat = new TextFormat("Arial", 12, match.getColor(), true);
			var nameField:TextField = new TextField();
			nameField.selectable = false;
			nameField.autoSize = TextFieldAutoSize.LEFT;
			nameField.x = 5;
			nameField.y = 2;
			nameField.defaultTextFormat = fieldFormat;
			nameField.text = "> " + match.getMotif().getName();
			mouseOverWindow.addChild(nameField);
			var logo:Sprite = match.getMotif().drawLogo();
			logo.x = 10;
			logo.y = 60;
			logo.scaleX = logo.scaleY = 0.2;
			mouseOverWindow.addChild(logo);
			fieldFormat.size = 9.5;
			fieldFormat.bold = false;
			fieldFormat.color = 0;
			var infoField:TextField = new TextField();
			infoField.selectable = false;
			infoField.autoSize = TextFieldAutoSize.LEFT;
			infoField.x = 5;
			infoField.y = 59;
			infoField.defaultTextFormat = fieldFormat;
			infoField.text = 
				"Score:\n" + match.getScore() + "\n" +
				"Sequence name:\n" + seqData.getRowName(match.getSequenceNumber()) + "\n" +
				"Position:\n" + match.getBeginIndex() + "-" + match.getEndIndex() + "\n" +
				"Strand:\n" + (match.getStrand() > 0 ? "positive" : "negative");
			mouseOverWindow.addChild(infoField);
		}
		
		/**
		 * When match gets MOUSE_OUT event
		 * @param	e
		 */
		private function mouseOutMotifMatch(e:MouseEvent):void {
			//var match:MotifMatch = e.target as MotifMatch;
			while (mouseOverWindow.numChildren > 1) mouseOverWindow.removeChildAt(1);
			mouseOverWindow.visible = false;
		}
		
		/**
		 * A function to process dragging.
		 */
		public override function processDragging():void {
			mouseOverWindow.x = mouseX + 15;
			mouseOverWindow.y = mouseY + 15;
			
			if (!viewDragged && !miniViewDragging) return;
			
			if (viewDragged) {
				view.x = mouseX - oldMouseCoordinates.x;
				view.y = mouseY - oldMouseCoordinates.y;
			}
			
			// The view 4 corners:
			if (view.x > 3 && globalLeftSymbolIndex == 0) view.x = 3;
			if (view.y > 3 && globalUpperRowIndex == 0) view.y = 3;
			if (view.y < 3 && globalUpperRowIndex >= seqData.getNumberOfSeqRows() - getNumberOfRowsOnView(zoomLevel)) view.y = 3;
			if (view.x < 3 && globalLeftSymbolIndex >= seqData.getSeqRowLength() - getNumberOfLettersInRow(zoomLevel)) view.x = 3;
			
			if (miniViewDragging) {
				miniViewRect.x += mouseX - oldMouseCoordinates.x;
				miniViewRect.y += mouseY - oldMouseCoordinates.y;
				oldMouseCoordinates.x = mouseX;
				oldMouseCoordinates.y = mouseY;
			}
			
			// The miniView 4 corners:
			if (miniViewRect.x > miniViewRect.maxX) miniViewRect.x = miniViewRect.maxX;
			if (miniViewRect.x < 0) miniViewRect.x = 0;
			if (miniViewRect.y > miniViewRect.maxY) miniViewRect.y = miniViewRect.maxY;
			if (miniViewRect.y < 0) miniViewRect.y = 0;
			
			// The names:
			if (seqRowNamesVisible) seqRowNames.x = -view.x + 6;
		}
		
		/**
		 * Called when dragging is stopped
		 * @param	e	-	mouse event
		 */
		function stopDragging(e:MouseEvent):void {
			if (!viewDragged && !miniViewDragging) return;
			
			if (viewDragged) {
				var horizontalChange:Number = (mouseX - oldMouseCoordinates.x) / getLetterSize(zoomLevel);
				globalLeftSymbolIndex -= Math.round(horizontalChange);
				var verticalChange:Number = (mouseY - oldMouseCoordinates.y) / (getLetterSize(zoomLevel) + getSpaceBetweenRows(zoomLevel));
				globalUpperRowIndex -= Math.round(verticalChange);
			}
			if (miniViewDragging) {
				if (seqData.getSeqRowLength() > getNumberOfLettersInRow(zoomLevel)) {
					globalLeftSymbolIndex = miniViewRect.x / miniViewRect.horizUnit;
				}
				if (seqData.getNumberOfSeqRows() > getNumberOfRowsOnView(zoomLevel)) {
					globalUpperRowIndex = miniViewRect.y / miniViewRect.vertUnit;
				}
			}
			
			var horizMax:int = seqData.getSeqRowLength() - getNumberOfLettersInRow(zoomLevel);
			var vertMax:int = seqData.getNumberOfSeqRows() - getNumberOfRowsOnView(zoomLevel);
			if (globalLeftSymbolIndex >= horizMax) globalLeftSymbolIndex = horizMax;
			if (globalUpperRowIndex >= vertMax) globalUpperRowIndex = vertMax;
			if (globalLeftSymbolIndex < 0) globalLeftSymbolIndex = 0;
			if (globalUpperRowIndex < 0) globalUpperRowIndex = 0;
			
			if (viewDragged) viewDragged = false;
			if (miniViewDragging) miniViewDragging = false;
			uiIsDirty = true;
			viewIsDirty = true;
		}
		
		/**
		 * Start dragging sequence view and process some other small mouseDown events
		 * @param	e
		 */
		private function dragSeqView(e:MouseEvent):void {
			if (zoomInRect != null && zoomInRect.parent == view) return;
			oldMouseCoordinates.x = mouseX;
			oldMouseCoordinates.y = mouseY;
			viewDragged = true;
		}
		
		/**
		 * Calls view or UI redrawing functions when neccessary
		 */
		public override function processRedrawing():void {
			if (viewIsDirty) {
				viewIsDirty = false;
				drawView();
				//System.gc();
			}
			if (uiIsDirty) {
				uiIsDirty = false;
				drawUI();
				//System.gc();
			}
		}
		
		/**
		 * Export view to PNG
		 * @param	scale	-	not implemented.
		 */
		public function exportView(scale:Number = 1):void {
			var s:Sprite = new Sprite();
			
			var viewBmd:BitmapData = new BitmapData(viewWidth, viewHeight, false, 0xFFFFFF);
			viewBmd.draw(view);
			s.addChild(new Bitmap(viewBmd));
			
			var legendWidth:Number = 130;
			var legend:Sprite = drawExportLegend(legendWidth);
			legend.x = viewWidth;
			s.addChild(legend);
			
			var bmd:BitmapData = new BitmapData(viewWidth + legendWidth, viewHeight, false, 0x000000);
			bmd.draw(s);
			
			var ba:ByteArray;
			ba = PNGEncoder.encode(bmd);
			
			var req:URLRequest = new URLRequest();
			var sVars:URLVariables = new URLVariables();
			
			sVars.image = Base64.encodeByteArray(ba);
			req.url = "imgexport.php";
			req.data = sVars;
			req.method = URLRequestMethod.POST;
			
			navigateToURL(req);
		}
		
		/**
		 * Draw the legend for exported image
		 * @param	legendWidth	-	the width
		 * @return				-	the Sprite with legend on
		 */
		private function drawExportLegend(legendWidth:Number):Sprite {
			var s:Sprite = new Sprite();
			s.graphics.beginFill(0xFFFFFF);
			s.graphics.drawRect(0, 0, legendWidth, viewHeight);
			s.graphics.endFill();
			//s.graphics.lineStyle(1, 0x555555);
			//s.graphics.moveTo(0, 0);
			//s.graphics.lineTo(0, viewHeight);
			
			var _y:Number = 0;
			
			// Motifs:
			var _length:int = motifMatchesArray.length;
			for (var i:int = 0; i < _length; i++) {
				var matches:MotifMatches = motifMatchesArray[i];
				if (matches.getHide()) continue;
				
				var name:TextField = new TextField();
				name.x = 5;
				name.y = _y;
				name.autoSize = TextFieldAutoSize.LEFT;
				name.selectable = false;
				name.defaultTextFormat = new TextFormat("Arial", 14, matches.getColor(), true);
				name.text = matches.getMotif().getName();
				s.addChild(name);
				
				var logo:Sprite = matches.getMotif().drawLogo();
				logo.scaleX = logo.scaleY = 0.2;
				logo.x = 15;
				_y += 60;
				logo.y = _y;
				_y += 10;
				s.addChild(logo);
			}
			
			// Tracks:
			_length = conservationParams.length;
			for (var j:int = 0; j < _length; j++) {
				var obj:Object = conservationParams[j];
				if (Boolean(obj.hidden)) continue;
				
				var trackName:TextField = new TextField();
				trackName.x = 5;
				trackName.y = _y;
				trackName.autoSize = TextFieldAutoSize.LEFT;
				trackName.selectable = false;
				trackName.defaultTextFormat = new TextFormat("Arial", 14, obj.color, true);
				trackName.text = obj.trackName;
				s.addChild(trackName);
				
				var trackLegend:Sprite = drawTrackLegend(zoomLevel, obj.color, Number(obj.maximum), String(obj.style));
				_y += 35;
				trackLegend.y = _y;
				_y += 35;
				trackLegend.x = 10;
				s.addChild(trackLegend);
			}
			
			return s;
		}
		
		// TODO: ühenda ConservationTab vastava funktsiooniga
		private function drawTrackLegend(zoomLevel:int, color:uint, max:Number, style:String):Sprite {
			var sp:Sprite = new Sprite();
			
			if (max < 1) max = 1; // TODO remove?
			var _xIncr:Number = getLetterSize(zoomLevel);
			var heightMultiplier:Number = 14;
			var _x:Number = 0; 
			var _value:Number = max;
			var _incr = max / (100/_xIncr);
			var g:Graphics = sp.graphics;
			var maxHeight:Number;
			if(style == "center") {
				while (_x < 100) {
					g.beginFill(color, _value);
					g.drawRect(_x, -1 * _value * heightMultiplier, _xIncr, 2 * _value * heightMultiplier);
					g.endFill();
					_value -= _incr;
					_x += _xIncr;
				}
				maxHeight = max * heightMultiplier;
				g.lineStyle(1);
				g.moveTo(0, -maxHeight);
				g.lineTo(0, maxHeight);
				g.moveTo(0, 0);
				g.lineTo(110, 0);
				sp.addChild(drawLabel(String(max), -8, -maxHeight - 4, true, 8));
				sp.addChild(drawLabel("0", -8, -4, true, 8));
				sp.addChild(drawLabel(String(max), -8, maxHeight - 4, true, 8));
			}
			else {
				while (_x < 100) {
					g.beginFill(color, _value * 0.33);
					g.drawRect(_x, (-1) * (_value * heightMultiplier), _xIncr, _value * heightMultiplier);
					g.endFill();
					g.lineStyle(1, color, 1, true, "normal", CapsStyle.ROUND);
					g.moveTo(_x, ( -1) * (_value * heightMultiplier));
					g.lineTo(_x + _xIncr, ( -1) * (_value * heightMultiplier));
					g.lineStyle();
					_value -= _incr;
					_x += _xIncr;
				}
				maxHeight = max * heightMultiplier;
				g.lineStyle(1);
				g.moveTo(0, -maxHeight);
				g.lineTo(0, 0);
				g.lineTo(110, 0);
				sp.addChild(drawLabel("0", -8, -4, true, 8));
				sp.addChild(drawLabel(String(max), -8, -maxHeight - 4, true, 8));
			}
			return sp;
		}
		
		/**
		 * Draw a formatted textfield.
		 * @param	_text	-	text on field
		 * @param	_x		-	x coordinate
		 * @param	_y		-	y coordinate
		 * @param	_bold	-	bold?
		 * @param	_size = 10	font size, default = 10
		 * @return
		 */
		private function drawLabel(_text:String, _x:Number = 0, _y:Number = 0, _bold:Boolean = true, _size = 10):TextField {
			var tf:TextField = new TextField();
			tf.x = _x;
			tf.y = _y
			tf.autoSize = TextFieldAutoSize.LEFT;
			tf.selectable = false;
			tf.defaultTextFormat = new TextFormat("Arial", _size, 0, _bold);
			tf.text = _text;
			return tf;
		}
		
	}
	
}